package com.baijiayun.liveuibase.widgets.dialog;

import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.LinearInterpolator;
import android.view.animation.RotateAnimation;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatImageView;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import com.baijiayun.livecore.context.LPConstants;
import com.baijiayun.livecore.context.LiveRoom;
import com.baijiayun.livecore.models.LPBleDevice;
import com.baijiayun.livecore.utils.LPLogger;
import com.baijiayun.livecore.utils.LPRxUtils;
import com.baijiayun.liveuibase.R;
import com.baijiayun.liveuibase.base.BaseDialogFragment;
import com.baijiayun.liveuibase.utils.DisplayUtils;
import com.baijiayun.liveuibase.utils.ThemeDataUtil;
import com.baijiayun.liveuibase.utils.drawable.DrawableBuilder;
import com.zxyb.zxybbaselib.ble.entity.BleDevice;

import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.PriorityQueue;
import java.util.concurrent.TimeUnit;

import android_serialport_api.DeviceInfo;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;

/**
 * @author yongjiaming
 * @date 2021/5/28
 * @description 蓝牙连接状态展示弹框
 */
public class BluetoothDeviceDialog extends BaseDialogFragment {

    // ‒ 退出搜索条件 1：当回调设备是 white-list 中最后一次连接的设备时，停止搜索设备，直接发起连接
    // ‒ 退出搜索条件 2：当回调设备发生重复时，可认为周围的设备都已发现，此时如果有一个或多个是 white-list 中的设备，则选取最后一次连接时间较近的发起连接
    // ‒ 退出搜索条件 3：搜索持续 10 秒（超时时间 B）但没有满足上面两个退出条件，则弹出设备列表，让用户手动选择设备
    private static final int BLUETOOTH_SCAN_TIMEOUT = 10;
    //‒ 已加入列表的设备，如果 5 秒（超时时间 A）内没有再次收到此设备的回调，则从列表中移除
    private static final int BLUETOOTH_SCAN_CHECK_TIME = 5;
    private static final String BLUETOOTH_DIV_TAG = "tag";
    private final BluetoothAdapter bluetoothAdapter = new BluetoothAdapter();
    //‒ 列表中，优先显示 white-list 中的设备，按最后一次连接时间排序
    //‒ 列表中，在 white-list 之后显示 unknown 设备，按设备序列号排序
    //设备列表
    private final List<LPBleDevice> bleDeviceList = new ArrayList<>();
    //白名单设备----不一定全部显示在设备列表中
    private final List<LPBleDevice> bleDeviceWhitelist = new ArrayList<>();
    //搜索列表
    private final List<LPBleDevice> scanningBleDeviceList = new ArrayList<>();
    //其他设备列表
    private final PriorityQueue<LPBleDevice> otherDeviceQueue = new PriorityQueue<>(11, (o1, o2) -> 0);
    private Disposable timerDisposable;
    private CompositeDisposable disposables;
    private boolean isScanning = false;
    private Context context;
    private final LiveRoom liveRoom;

    public BluetoothDeviceDialog(LiveRoom liveRoom) {
        this.liveRoom = liveRoom;
    }

    @Override
    public void onAttach(@NonNull Context context) {
        super.onAttach(context);
        this.context = context;
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        title(context.getString(R.string.base_live_bluetooth));
        showCross();
        RecyclerView recyclerView = $.id(R.id.base_bluetooth_recyclerView).view();
        recyclerView.setAdapter(bluetoothAdapter);
        recyclerView.setLayoutManager(new LinearLayoutManager(context));
        disposables = new CompositeDisposable();
        intZXYBListener();
        startScan();
    }

    @Override
    public void onStart() {
        super.onStart();
        final Dialog dialog = getDialog();
        if (dialog != null) {
            dialog.setCanceledOnTouchOutside(false);
            dialog.setCancelable(false);
            dialog.getWindow().setBackgroundDrawable(new DrawableBuilder()
                    .rectangle()
                    .solidColor(ThemeDataUtil.getColorFromThemeConfigByAttrId(context, R.attr.base_theme_window_bg_color))
                    .cornerRadius(context.getResources().getDimensionPixelSize(R.dimen.bjy_base_common_bg_radius))
                    .build());
            setTitleBackground(new DrawableBuilder()
                    .rectangle()
                    .solidColor(ThemeDataUtil.getColorFromThemeConfigByAttrId(context, R.attr.base_theme_window_bg_color))
                    .topLeftRadius(context.getResources().getDimensionPixelSize(R.dimen.bjy_base_common_bg_radius))
                    .topRightRadius(context.getResources().getDimensionPixelSize(R.dimen.bjy_base_common_bg_radius))
                    .build());
        }
    }

    @Override
    protected void setWindowParams(WindowManager.LayoutParams windowParams) {
        windowParams.width = DisplayUtils.dip2px(context, 460);
        windowParams.height = DisplayUtils.dip2px(context, 285);
        windowParams.gravity = Gravity.CENTER;
    }

    private void startScan() {
        liveRoom.getZXYBVM().startScan();
        isScanning = true;
        //检查超时和自动连接
        LPRxUtils.dispose(timerDisposable);
        timerDisposable = Observable.interval(1, 1, TimeUnit.SECONDS)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(aLong -> {
                    if (aLong == BLUETOOTH_SCAN_TIMEOUT - 1) {
                        liveRoom.getZXYBVM().stopScan();
                        LPRxUtils.dispose(timerDisposable);
                    }
                    if (isScanning) {
                        checkBleDeviceAvailable();
                    }
                });
    }

    public void refresh() {
        startScan();
    }

    /**
     * 检查5s内是否扫描到已加入列表的设备
     * 5s内没有搜索到则移出设备列表
     */
    private void checkBleDeviceAvailable() {
        final Iterator<LPBleDevice> deviceIterator = bleDeviceList.iterator();
        boolean isChange = false;
        while (deviceIterator.hasNext()) {
            final LPBleDevice bleDevice = deviceIterator.next();
            final long currentTimeMillis = System.currentTimeMillis();
            if (!BLUETOOTH_DIV_TAG.equals(bleDevice.address)
                    && currentTimeMillis - bleDevice.getScanTimestamp() > BLUETOOTH_SCAN_CHECK_TIME * 1000
                    && bleDevice.status != LPConstants.ZXYBConnectStatus.CONNECT_STATUS_SUCCESS) {
                deviceIterator.remove();
                otherDeviceQueue.remove(bleDevice);
                final int index = bleDeviceWhitelist.indexOf(bleDevice);
                if (index != -1) {
                    bleDeviceWhitelist.get(index).isActive = false;
                }
                isChange = true;
            }
        }
        if (isChange) {
            bluetoothAdapter.notifyDataSetChanged();
        }
    }

    /**
     * @param recentConnectDevices 最近连接过的设备
     * @return
     */
    public BluetoothDeviceDialog setData(List<LPBleDevice> recentConnectDevices) {
        bleDeviceList.clear();
        otherDeviceQueue.clear();
        bleDeviceWhitelist.clear();
        long currentTimeMillis = System.currentTimeMillis();
        for (LPBleDevice lpBleDevice : recentConnectDevices) {
            lpBleDevice.setScanTimestamp(currentTimeMillis);
        }
        bleDeviceWhitelist.addAll(recentConnectDevices);
        bleDeviceList.addAll(recentConnectDevices);
        bleDeviceList.add(new LPBleDevice(BLUETOOTH_DIV_TAG, BLUETOOTH_DIV_TAG));
        bluetoothAdapter.notifyDataSetChanged();
        return this;
    }

    private void intZXYBListener() {
        disposables.add(liveRoom.getZXYBVM().getObservableOfScanStatus()
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(aBoolean -> {
                    LPLogger.d("zxyb-ble", "scan:" + aBoolean);
                    if (aBoolean) {
                        isScanning = true;
                    } else {
                        LPRxUtils.dispose(timerDisposable);
                        isScanning = false;
                    }
                    bluetoothAdapter.notifyDataSetChanged();
                }));
        disposables.add(liveRoom.getZXYBVM().getObservableOfScanDevice()
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(this::onScanDevice));
        disposables.add(liveRoom.getZXYBVM().getObservableOfConnectStatus()
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(zxybConnectStatus -> {
                    if (zxybConnectStatus == LPConstants.ZXYBConnectStatus.CONNECT_STATUS_SUCCESS) {
                        DeviceInfo deviceInfo = liveRoom.getZXYBVM().getConnectedDevice();
                        if (deviceInfo == null) {
                            return;
                        }
                        for (LPBleDevice lpBleDevice : bleDeviceList) {
                            lpBleDevice.status = lpBleDevice.address.equals(deviceInfo.bleAdress) ?LPConstants.ZXYBConnectStatus.CONNECT_STATUS_SUCCESS : LPConstants.ZXYBConnectStatus.CONNECT_STATUS_UNIT;
                        }
                        bluetoothAdapter.notifyDataSetChanged();
                    } else if (zxybConnectStatus == LPConstants.ZXYBConnectStatus.CONNECT_STATUS_FAIL || zxybConnectStatus == LPConstants.ZXYBConnectStatus.CONNECT_STATUS_DISCONNECT) {
                        for (LPBleDevice lpBleDevice : bleDeviceList) {
                            lpBleDevice.status = LPConstants.ZXYBConnectStatus.CONNECT_STATUS_UNIT;
                        }
                        bluetoothAdapter.notifyDataSetChanged();
                    }
                }));
    }

    private void onScanDevice(BleDevice bleDevice) {
        LPLogger.d("zxyb-ble", "scan:" + bleDevice.name + "----" + bleDevice.address);
        LPBleDevice lpBleDevice = new LPBleDevice(bleDevice);
        final long currentTimeMillis = System.currentTimeMillis();
        lpBleDevice.setScanTimestamp(currentTimeMillis);
        if (!bleDeviceList.contains(lpBleDevice)) {
            if (!otherDeviceQueue.contains(lpBleDevice) && !bleDeviceWhitelist.contains(lpBleDevice)) {
                otherDeviceQueue.add(lpBleDevice);
            }
            if (bleDeviceWhitelist.contains(lpBleDevice)) {
                final int index = bleDeviceWhitelist.indexOf(lpBleDevice);
                final LPBleDevice whitelistDevice = bleDeviceWhitelist.get(index);
                whitelistDevice.isActive = true;
            }
            bleDeviceList.add(lpBleDevice);
            bluetoothAdapter.notifyDataSetChanged();
        } else {
            //设备列表存在则替换搜索时间
            final int index = bleDeviceList.indexOf(lpBleDevice);
            if (index != -1) {
                //这里不用set，可能改变时间戳
                bleDeviceList.get(index).setScanTimestamp(currentTimeMillis);
            }
            if (otherDeviceQueue.remove(lpBleDevice)) {
                otherDeviceQueue.add(lpBleDevice);
            } else if (bleDeviceWhitelist.contains(lpBleDevice)) {
                final int i = bleDeviceWhitelist.indexOf(lpBleDevice);
                if (i != -1) {
                    final LPBleDevice whitelistDevice = bleDeviceWhitelist.get(i);
                    whitelistDevice.setScanTimestamp(currentTimeMillis);
                    whitelistDevice.isActive = true;
                }
            }
        }
        boolean isConnect = false;
        final int index = bleDeviceWhitelist.indexOf(lpBleDevice);
        if (index == 0) {
            connectDevice(lpBleDevice);
            liveRoom.getZXYBVM().stopScan();
            isConnect = true;
        }
        if (!scanningBleDeviceList.contains(lpBleDevice)) {
            scanningBleDeviceList.add(lpBleDevice);
        } else {
            if (isConnect) {
                return;
            }
            //搜索到重复设备,是白名单列表中数据
            if (!bleDeviceWhitelist.isEmpty()) {
                LPBleDevice autoConnectDevice = null;
                for (LPBleDevice device : bleDeviceWhitelist) {
                    if (device.isActive) {
                        autoConnectDevice = device;
                        break;
                    }
                }
                if (autoConnectDevice != null) {
                    connectDevice(lpBleDevice);
                    liveRoom.getZXYBVM().stopScan();
                }
            }
        }
    }

    private void insertConnectedDevice(LPBleDevice bleDevice) {
        bleDevice.updateConnectTime();
        otherDeviceQueue.remove(bleDevice);
        bleDeviceList.remove(bleDevice);
        bleDeviceWhitelist.add(0, bleDevice);
        bleDeviceList.add(0, bleDevice);
        bluetoothAdapter.notifyDataSetChanged();
    }

    private void connectDevice(LPBleDevice bleDevice) {
        bleDevice.status = LPConstants.ZXYBConnectStatus.CONNECT_STATUS_CONNECTING;
        for (LPBleDevice lpBleDevice : bleDeviceList) {
            if (!bleDevice.equals(lpBleDevice)) {
                lpBleDevice.status = LPConstants.ZXYBConnectStatus.CONNECT_STATUS_UNIT;
            }
        }
        insertConnectedDevice(bleDevice);
        liveRoom.getZXYBVM().connectZXYB(bleDevice.address);
    }

    @Override
    public void dismiss() {
        super.dismiss();
        onDestroyView();
    }


    @Override
    public void onDestroyView() {
        super.onDestroyView();
        liveRoom.getZXYBVM().stopScan();
        LPRxUtils.dispose(timerDisposable);
        LPRxUtils.dispose(disposables);
    }

    @Override
    protected int getLayoutId() {
        return R.layout.uibase_bluetooth_list_dialog;
    }

    @Override
    protected void init(Bundle savedInstanceState, Bundle arguments) {
        // do nothing
    }

    public class BluetoothAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

        private static final int DIVIDER_TYPE = 0;
        private static final int BLUETOOTH_TYPE = 1;

        @NonNull
        @NotNull
        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(@NonNull @NotNull ViewGroup parent, int viewType) {
            if (viewType == DIVIDER_TYPE) {
                View itemView = LayoutInflater.from(getContext()).inflate(R.layout.uibase_bluetooth_divider, parent, false);
                return new DividerViewHolder(itemView);
            } else {
                View itemView = LayoutInflater.from(getContext()).inflate(R.layout.uibase_bluetooth_item, parent, false);
                return new ViewHolder(itemView);
            }
        }

        @Override
        public void onBindViewHolder(@NonNull @NotNull RecyclerView.ViewHolder holder, int position) {
            if (getItemViewType(position) == BLUETOOTH_TYPE) {
                ViewHolder viewHolder = (ViewHolder) holder;
                LPBleDevice bleDevice = bleDeviceList.get(position);
                viewHolder.deviceNameTv.setText(bleDevice.name);
                if (bleDevice.status == LPConstants.ZXYBConnectStatus.CONNECT_STATUS_SUCCESS) {
                    viewHolder.showConnected();
                } else if (bleDevice.status == LPConstants.ZXYBConnectStatus.CONNECT_STATUS_CONNECTING) {
                    viewHolder.showLoading();
                } else {
                    viewHolder.showInitStatus(bleDevice);
                }
                viewHolder.itemView.setOnClickListener(v -> connectDevice(bleDevice));
            } else {
                DividerViewHolder viewHolder = (DividerViewHolder) holder;
                if (isScanning) {
                    viewHolder.imageView.setVisibility(View.VISIBLE);
                    viewHolder.animator.start();
                } else {
                    viewHolder.imageView.setVisibility(View.GONE);
                    viewHolder.animator.cancel();
                }
            }
        }

        @Override
        public int getItemCount() {
            return bleDeviceList.size();
        }

        @Override
        public int getItemViewType(int position) {
            return BLUETOOTH_DIV_TAG.equals(bleDeviceList.get(position).address) ? DIVIDER_TYPE : BLUETOOTH_TYPE;
        }

        class ViewHolder extends RecyclerView.ViewHolder {

            TextView deviceNameTv;
            TextView connectStatusTv;
            AppCompatImageView connectStatusIv;
            RotateAnimation animator;

            public ViewHolder(@NonNull @NotNull View itemView) {
                super(itemView);
                deviceNameTv = itemView.findViewById(R.id.ui_base_ble_device_name);
                connectStatusTv = itemView.findViewById(R.id.ui_base_ble_device_status_tv);
                connectStatusIv = itemView.findViewById(R.id.ui_base_ble_device_status_iv);
                animator = new RotateAnimation(0, 360f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
                animator.setDuration(1500);
                animator.setInterpolator(new LinearInterpolator());
                animator.setRepeatCount(ValueAnimator.INFINITE);
            }

            public void showLoading() {
                connectStatusIv.setVisibility(View.VISIBLE);
                connectStatusIv.setImageResource(R.drawable.base_course_ware_loading);
                connectStatusTv.setVisibility(View.GONE);
                connectStatusIv.setAnimation(animator);
                animator.start();
            }

            public void showConnected() {
                connectStatusIv.setVisibility(View.VISIBLE);
                connectStatusIv.setImageResource(R.drawable.base_live_connected_checkbox);
                connectStatusTv.setVisibility(View.GONE);
                animator.cancel();
                connectStatusIv.clearAnimation();
            }

            public void showInitStatus(LPBleDevice bleDevice) {
                connectStatusIv.setVisibility(View.GONE);
                connectStatusTv.setText(context.getString(R.string.base_live_not_connected));
                connectStatusTv.setVisibility(bleDevice.isWhitelistDevice() ? View.VISIBLE : View.GONE);
                animator.cancel();
                connectStatusIv.clearAnimation();
            }
        }

        class DividerViewHolder extends RecyclerView.ViewHolder {
            ImageView imageView;
            ObjectAnimator animator;

            public DividerViewHolder(View itemView) {
                super(itemView);
                imageView = itemView.findViewById(R.id.window_course_ware_loading);
                animator = ObjectAnimator.ofFloat(imageView, "rotation", 0, 360);
                animator.setRepeatCount(ValueAnimator.INFINITE);
                animator.setInterpolator(new LinearInterpolator());
                animator.setDuration(1500);
            }
        }
    }
}
